/*
 * Copyright (c) 2010-2011, Petr Panteleyev <petr@panteleyev.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.flickr;

import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;

import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JMenu;
import javax.swing.SwingUtilities;
import javax.swing.text.html.HTML;

import org.panteleyev.blogclient.BloggerEditorFrame;
import org.panteleyev.blogclient.HandleExceptionEDT;
import org.panteleyev.blogclient.MainClass;
import org.panteleyev.blogclient.UserProfileManager;
import org.panteleyev.blogclient.photo.ThumbnailPanel;
import org.panteleyev.flickrapi.FlickrAccount;
import org.panteleyev.flickrapi.FlickrException;
import org.panteleyev.flickrapi.FlickrPhoto;
import org.panteleyev.flickrapi.FlickrPhotoInfo;
import org.panteleyev.flickrapi.FlickrPhotoSet;
import org.panteleyev.flickrapi.MediaType;
import org.panteleyev.flickrapi.PhotoSize;
import org.panteleyev.flickrapi.PhotoSizeType;
import org.panteleyev.flickrapi.PrivacyFilter;
import org.panteleyev.flickrapi.SizeTable;
import org.panteleyev.photo.Thumbnail;
import org.panteleyev.swing.ClipboardUtil;
import org.panteleyev.swing.MenuProvider;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.SwingFactory;
import org.panteleyev.swing.ToolBarProvider;
import org.panteleyev.utilities.Updatable;

import com.google.common.collect.ImmutableList;

final class FlickrSetPanel extends ThumbnailPanel<PhotoSetListFrame,FlickrPhoto> implements Updatable, MenuProvider, ToolBarProvider {
    private FLUserProfile       profile;
    private FlickrAccount       account;
    private FlickrPhotoSet      photoSet;
    private AbstractAction      picturePropertiesAction = new PicturePropertiesAction();
    private AbstractAction      pictureOpenWebAction = new PictureOpenWebAction();
    private AbstractAction      picturePublishAction = new PublichPucturesAction();
    private ArrayList<AbstractAction> toolbarActions;

    private final Runnable finishLongOperationEDT = new Runnable() {
        @Override
        public void run() {
            finishLongOperation();
        }
    };

    @Override
    protected int getCellWidth() {
        return 110;
    }

    private class UpdatePhotosThread extends Thread {
        private final boolean useCache;

        UpdatePhotosThread(boolean useCache) {
            this.useCache = useCache;
            startLongOperation();
        }

        @Override
        public void run() {
            try {
                setPictures(account.getPhotos(photoSet, PrivacyFilter.ALL, MediaType.PHOTOS));
                updateThumbnails();
            } catch (Exception ex) {
                SwingUtilities.invokeLater(new HandleExceptionEDT(getFrame(), ex));
            } finally {
                SwingUtilities.invokeLater(finishLongOperationEDT);
            }
        }

        private void updateThumbnails() throws FlickrException, IOException {
            getThumbnails().clear();
            getThumbnails().ensureCapacity(getPictures().size());

            for (FlickrPhoto photo : getPictures()) {
                /* Get photo info */
                FlickrPhotoInfo info = (useCache)? getProfile().getPhotoInfo(photo) : null;
                if (info == null) {
                    info = account.getPhotoInfo(photo);
                    getProfile().addPhotoInfo(info);
                }

                /* Get photo sizes */
                SizeTable sizes = (useCache)? getProfile().getPhotoSizes(photo) : null;
                if (sizes == null) {
                    sizes = account.getSizes(photo);
                    getProfile().addPhotoSize(photo, sizes);
                }

                PhotoSize size = sizes.get(PhotoSizeType.THUMBMNAIL);
                final URL url = new URL(size.getSource());
                ImageIcon thumb = (useCache)? UserProfileManager.loadThumbnail(getProfile(), url) : null;
                if (thumb == null) {
                    final URLConnection conn = url.openConnection();
                    BufferedImage bim = ImageIO.read(conn.getInputStream());
                    thumb = new ImageIcon(bim);
                    UserProfileManager.storeThumbnail(getProfile(), url, bim);
                }
                getThumbnailsLock().lock();
                try {
                    getThumbnails().add(thumb);
                } finally {
                    getThumbnailsLock().unlock();
                }
                SwingUtilities.invokeLater(getUpdateWindowEDT());
            }
            getProfile().storePhotoInfo();
            getProfile().storePhotoSizes();
        }
    }

    private class SetMetaThread extends Thread {
        private FlickrPhotoInfo info;
        private String title;
        private String description;

        public SetMetaThread(FlickrPhotoInfo info, String title, String description) {
            this.info = info;
            this.title = title;
            this.description = description;
            startLongOperation();
        }

        @Override
        public void run() {
            try {
                account.setPhotoMeta(info.getId(), title, description);
                info.setTitle(title);
                info.setDescription(description);
                getProfile().storePhotoInfo();
                SwingUtilities.invokeLater(finishLongOperationEDT);
            } catch (Exception ex) {
                SwingUtilities.invokeLater(new HandleExceptionEDT(getFrame(), ex));
            }
        }
    }

    /** Creates new form FlickrSetFrame */
    public FlickrSetPanel(PhotoSetListFrame frame, FLUserProfile profile, FlickrPhotoSet photoSet) {
        super(frame);
        this.profile = profile;
        this.account = profile.getAccount();
        this.photoSet = photoSet;

        createActions();
        initComponents();

        update();
    }

    private FLUserProfile getProfile() {
        return profile;
    }

    FlickrPhotoSet getPhotoSet() {
        return photoSet;
    }

    private void createActions() {
        toolbarActions = new ArrayList<AbstractAction>();
        toolbarActions.add(pictureOpenWebAction);
    }

    private void initComponents() {
        setLayout(new BorderLayout());
        add(getScroller(), BorderLayout.CENTER);
        getScroller().setComponentPopupMenu(createMenu().getPopupMenu());
    }

    @Override
    public JMenu createMenu() {
        return SwingFactory.createMenu(PHOTO_MENU_NAME,
                    picturePropertiesAction,
                    null,
                    pictureOpenWebAction,
                    picturePublishAction);
    }

    @Override
    public Collection<AbstractAction> getToolBarActions() {
        return toolbarActions;
    }

    private void startLongOperation() {
        getFrame().startLongOperation();
        picturePropertiesAction.setEnabled(false);
        pictureOpenWebAction.setEnabled(false);
        getFrame().getUpdateAction().setEnabled(false);
    }

    private void finishLongOperation() {
        getFrame().getUpdateAction().setEnabled(true);
        picturePropertiesAction.setEnabled(true);
        pictureOpenWebAction.setEnabled(true);
        getFrame().finishLongOperation();
    }

    @Override
    public void update() {
        new UpdatePhotosThread(true).start();
    }

    @Override
    protected void onPictureProperties(FlickrPhoto picture) {
        final FlickrPhotoInfo info = getProfile().getPhotoInfo(picture);

        final FlickrPhotoDialog dial = new FlickrPhotoDialog(getFrame(), info);
        if (dial.showDialog()) {
            new SetMetaThread(info, dial.getPhotoTitle(), dial.getDescription()).start();
        }
    }

    @Override
    protected void pictureDoubleClicked(FlickrPhoto photo) {
        FlickrPhotoInfo info = getProfile().getPhotoInfo(photo);
        if (info != null) {
            OSUtil.runBrowser(info.getPhotoPage());
        }
    }

    @Override
    protected void onPublishPictures(ImmutableList<FlickrPhoto> pictures) {
        PublishPicturesDialog dial = new PublishPicturesDialog(getFrame(), getProfile(), pictures);
        if (dial.showDialog()) {
            try {
                getFrame().startLongOperation();

                StringBuilder b = new StringBuilder();

                if (dial.isUseTable()) {
                    b.append("<table");
                    if (dial.getTableBorder()) {
                        b.append(" border='1'");
                    }
                    b.append(">");
                }

                int nCols = Math.min(dial.getColumnCount(), pictures.size());
                int nRows = pictures.size() / nCols + (int)Math.signum(pictures.size() % nCols);

                int index = 0;

                PhotoSizeType sizeType = dial.getThumbnailSize();

                for (int row = 0; row < nRows; row++) {
                    if (index >= pictures.size()) {
                        break;
                    }

                    if (dial.isUseTable()) {
                        b.append("<tr valign='top' align='center'>");
                    }

                    for (int col = 0; col < nCols; col++) {
                        if (index >= pictures.size()) {
                            break;
                        }

                        final Thumbnail<FlickrPhoto> thumbnail = dial.getThumbnail(index++);
                        final FlickrPhoto pict = thumbnail.getPayload();
                        final String pictTitle = thumbnail.getTitle();

                        FlickrPhotoInfo info = getProfile().getPhotoInfo(pict);

                        if (dial.isUseTable()) {
                            b.append("<td>");
                        }

                        b.append("<a href='");
                        b.append(info.getPhotoPage());
                        b.append("'>");

                        SizeTable sizes = getProfile().getPhotoSizes(pict);
                        if (sizes != null) {
                            PhotoSize size = sizes.get(sizeType.value());
                            String thumbUrl = size.getSource();

                            b.append("<img src='")
                                .append(thumbUrl)
                                .append("' border='")
                                .append((dial.getImageBorder())? "1" : "0")
                                .append("' width='")
                                .append(size.getWidth().toString())
                                .append("' title='")
                                .append(pictTitle)
                                .append("' height='")
                                .append(size.getHeight().toString())
                                .append("'>");
                        }
                        b.append("</a>");

                        if (dial.isUseTable()) {
                            if (dial.isAddTitle() && pictTitle != null && !pictTitle.isEmpty()) {
                                b.append("<br>");
                                b.append(pictTitle);
                            }
                            b.append("</td>");
                        } else {
                            b.append("\n");
                        }
                    }

                    if (dial.isUseTable()) {
                        b.append("</tr>");
                    }
                }

                if (dial.isUseTable()) {
                    b.append("</table>");
                }

                BloggerEditorFrame<?> destWnd = dial.getDestinationWindow();
                if (destWnd == null) {
                    ClipboardUtil.put(b.toString());
                } else {
                    if (dial.isUseTable()) {
                        destWnd.getEditor().insertHTML(b.toString(), HTML.Tag.TABLE);
                    } else {
                        destWnd.getEditor().insertText(b.toString());
                    }
                }
            } catch (Exception ex) {
                MainClass.handleException(getFrame(), ex);
            } finally {
                getFrame().finishLongOperation();
            }
        }
    }
}
